/**
    @project {
        "templateId": "org.helgoboss.custom",
        "version": "0.1-SNAPSHOT",
        "dependencies": [
            "org.scala-lang:scala-library:2.9.0-1"
        ]
    }
*/

package org.helgoboss.jvm_commandlet_client

import java.io.{File, InputStream, OutputStream, OutputStreamWriter, PrintWriter, FileInputStream, IOException}
import java.net.{Socket, URLEncoder}
import scala.io.Source
import java.util.Properties

object JvmCommandletClient {
    val ConfigFile = new File(System.getProperty("user.home") + "/.commandlet-platform/config.properties")
}

class JvmCommandletClient {

    private val properties = new Properties
    private var propertiesLoaded = false
    
    def executeCommandlet(commandletId: String, workingDirectory: File, arguments: Array[String], input: InputStream, output: OutputStream): Int = {
        /* Construct request */
        
        val encode = URLEncoder.encode(_: String, "UTF-8")
        val argString = arguments.map { "arg=" + encode(_) }.mkString("&")
        val path = contextPath + "?id=" + encode(commandletId) + "&wd=" + encode(workingDirectory.getAbsolutePath) + "&" + argString
        val request = "POST " + path + " HTTP/1.0\r\n\r\n"
        val outputWriter = new PrintWriter(new OutputStreamWriter(output), true)
        
        try {
            /* Open socket and send request */
            
            val socket = new Socket(host, port)
            val socketWriter = new OutputStreamWriter(socket.getOutputStream)
            socketWriter.write(request)
            socketWriter.flush
            
            
            /* Extract HTTP status code */
            
            val socketInputStream = socket.getInputStream
            val firstLine = Source.fromInputStream(socketInputStream).getLines().next
            val httpStatusCode = firstLine.split(" ")(1).toInt
            
            
            /* Skip remaining header */
            
            readUntil(socketInputStream, "\r\n\r\n")
            
            
            /* Print response incrementally */
            
            
            Source.fromInputStream(socketInputStream).getLines().foreach { line =>
                outputWriter.println(line)
            }
            
            socketWriter.close
            
            
            /* Return result code 0 if HTTP status code is 200 (OK), otherwise return the HTTP status code */
            
            if (httpStatusCode == 200) {
                0
            } else {
                httpStatusCode
            }
        } catch {
            case e: IOException =>
                outputWriter.println("Unable to connect to Commandlet HTTP Provider")
                1
        } finally {
            outputWriter.close
        }
    }
    
    
    private def readUntil(inputStream: InputStream, searchString: String) {
        var position = 0
        var endOfStreamReached = false
        
        while (position < searchString.length && !endOfStreamReached) {
            val c = inputStream.read
            if (c == -1) {
                endOfStreamReached = true
            } else {
                if(searchString(position) == c) {
                    position += 1
                } else {
                    position = 0
                }
            }
        }
    }
        
    private def getConfigValue(key: String): Option[String] = {
        if (!propertiesLoaded) {
            if (JvmCommandletClient.ConfigFile.exists) {
                properties.load(new FileInputStream(JvmCommandletClient.ConfigFile))
            }
            propertiesLoaded = true
        }
        
        val configValue = properties.getProperty(key)
        
        if (configValue == null) {
            None
        } else {
            Some(configValue)
        }
    }
    
    private def host = {
        getConfigValue("host").getOrElse("localhost")
    }
    
    private def port = {
        getConfigValue("port") match {
            case Some(port) =>
                port.toInt
            
            case None =>
                8181
        }
    }
    
    private def contextPath = {
        getConfigValue("context_path").getOrElse("/commandlet/execute")
    }
}